# package deb

# import (
# 	"bufio"
# 	"fmt"
# 	"os"
# 	"path"
# 	"path/filepath"
# 	"strings"

# 	"github.com/aptly-dev/aptly/aptly"
# 	"github.com/aptly-dev/aptly/pgp"
# 	"github.com/aptly-dev/aptly/utils"
# )
from deb.package import *
from utils.checksum import *
from utils.compress import *
from pgp.gnupg import *
import aptly
class indexFile:
	parent        =None#*indexFiles
	discardable   :bool=None
	compressable  :bool=None
	onlyGzip      :bool=None
	clearSign     :bool=None
	detachedSign  :bool=None
	acquireByHash :bool=None
	relativePath  :str=''
	tempFilename  :str=''
	tempFile      =None#*os.File
	w             =None#*bufio.Writer


	def BufWriter(file) :
		if file.w is None  :
			err=None
			file.tempFilename = os.path.join(file.parent.tempDir, file.relativePath.replace( "/", "_" ))
			file.tempFile  = file.tempFilename
			if err is not None  :
				return None, "unable to create temporary index file: {}".format(err)
			file.w = open(file.tempFilename,'w')
		return file.w, None
	

	def Finalize(file,signer :GpgSigner  ) :
		if file.w is None  :
			if file.discardable :
				return None
			
			file.BufWriter()
		
		err = file.w.flush()

		if err is not None  :
			file.w.close()
			return "unable to write to index file: {}".format (err)
		

		if file.compressable :
			err = CompressFile(file.tempFile, file.onlyGzip)
			if err is not None  :
				file.w.close()
				return  "unable to compress index file: {}".format(err)
			
		

		

		exts = ['']#string{""}
		cksumExts = exts
		if file.compressable :
			exts=exts+  [".gz", ".bz2"]
			cksumExts = exts
			if file.onlyGzip :
				exts = [".gz"]#string{}
				cksumExts = ["", ".gz"]#string{}
			
		for   ext in cksumExts :
			checksumInfo :ChecksumInfo=None

			checksumInfo =  ChecksumsForFile(file.tempFilename + ext)
			# if err is not None  :
			# 	return fmt.Errorf("unable to collect checksums: %s", err)
			# }
			file.parent.generatedFiles[file.relativePath+ext] = checksumInfo
		# }
		
		filedir = os.path.dirname(os.path.join(file.parent.basePath, file.relativePath))

		err = file.parent.publishedStorage.MkDir(filedir)
		if err is not None  :
			return  "unable to create dir: {}".format( err)
		

		if file.acquireByHash :
			for _, hash in ["MD5Sum", "SHA1", "SHA256", "SHA512"]:
				err = file.parent.publishedStorage.MkDir(os.path.join(filedir, "by-hash", hash))
				if err is not None  :
					return  "unable to create dir: {}".format( err)
				
			
		

		for  ext in exts :
			err = file.parent.publishedStorage.PutFile(os.path.join(file.parent.basePath, file.relativePath+file.parent.suffix+ext),
				file.tempFilename+ext)
			if err is not None  :
				return  "unable to publish file: {}".format(err)
			

			if file.parent.suffix != "" :
				file.parent.renameMap[os.path.join(file.parent.basePath, file.relativePath+file.parent.suffix+ext)] = \
					os.path.join(file.parent.basePath, file.relativePath+ext)
			

			if file.acquireByHash :
				sums = file.parent.generatedFiles[file.relativePath+ext]
				dataDict={"SHA512": sums.SHA512, "SHA256": sums.SHA256, "SHA1": sums.SHA1, "MD5Sum": sums.MD5}
				for hash, sum in dataDict.items():
					err = packageIndexByHash(file, ext, hash, sum)
					if err is not None  :
						return  "unable to build hash file: {}".format(err)
					
				
			
		

		if signer is not None  :
			if file.detachedSign :
				
				err = signer.DetachedSign(file.tempFilename, file.tempFilename+".gpg")
				if err is not None  :
					return  "unable to detached sign file: {}".format(err)
				
				
				if file.parent.suffix != "" :
					file.parent.renameMap[os.path.join(file.parent.basePath, file.relativePath+file.parent.suffix+".gpg")] = \
						os.path.join(file.parent.basePath, file.relativePath+".gpg")
				

				err = file.parent.publishedStorage.PutFile(os.path.join(file.parent.basePath, file.relativePath+file.parent.suffix+".gpg"),
					file.tempFilename+".gpg")
				if err is not None  :
					return  "unable to publish file: {}".format(err)
				

			

			if file.clearSign :
				err = signer.ClearSign(file.tempFilename, os.path.join(os.path.dirname(file.tempFilename), "In"+os.path.basename(file.tempFilename)))
				if err is not None  :
					return  "unable to clearsign file: {}".format(err)
				

				if file.parent.suffix != "" :
					file.parent.renameMap[os.path.join(file.parent.basePath, "In"+file.relativePath+file.parent.suffix)] = \
						os.path.join(file.parent.basePath, "In"+file.relativePath)
				

				err = file.parent.publishedStorage.PutFile(os.path.join(file.parent.basePath, "In"+file.relativePath+file.parent.suffix),
					os.path.join(os.path.dirname(file.tempFilename), "In"+os.path.basename(file.tempFilename)))
				if err is not None  :
					return "unable to publish file: {}".format(err)
				
			
		
		file.w.close()
		return None
	






class indexFiles :
	publishedStorage =None
	basePath         :str=None
	renameMap        ={}
	generatedFiles   ={}#map[string]utils.ChecksumInfo
	tempDir          :str=None
	suffix           :str=None
	indexes          ={}#map[string]*indexFile
	acquireByHash    =None#bool


	def  PackageIndex(files,component, arch :str, udeb :bool, installer :bool, distribution :str) ->indexFile :
		if arch == ArchitectureSource :
			udeb = False
		
		key =  "pi-{}-{}-{}-{}".format(component, arch, udeb, installer)
		file = files.indexes.get(key)
		if  file is  None :
			relativePath :str=''

			if arch == ArchitectureSource :
				relativePath = os.path.join(component, "source", "Sources")
			else :
				if udeb :
					relativePath = os.path.join(component, "debian-installer",  "binary-{}".format(arch), "Packages")
				elif installer :
					if distribution == aptly.DistributionFocal :
						relativePath = os.path.join(component, "installer-{}".format( arch), "current", "legacy-images", "SHA256SUMS")
					else :
						relativePath = os.path.join(component, "installer-{}".format( arch), "current", "images", "SHA256SUMS")
				
				else :
					relativePath = os.path.join(component, "binary-{}".format(arch), "Packages")
				
			
			file=indexFile()

			
			file.parent=        files
			file.discardable=   False
			file.compressable=  not installer
			file.detachedSign=  installer
			file.clearSign=     False
			file.acquireByHash= files.acquireByHash
			file.relativePath=  relativePath
			

			files.indexes[key] = file
		

		return file
	

	def  ReleaseIndex(files,component, arch :str, udeb :bool) ->indexFile :
		if arch == ArchitectureSource :
			udeb = False
		
		key =  "ri-{}-{}-{}".format(component, arch, udeb)
		file = files.indexes.get(key)
		if file is None:
			relativePath :str=''

			if arch == ArchitectureSource :
				relativePath = os.path.join(component, "source", "Release")
			else :
				if udeb :
					relativePath = os.path.join(component, "debian-installer",  "binary-{}".format(arch), "Release")
				else :
					relativePath = os.path.join(component,  "binary-{}".format(arch), "Release")
				
			
			file=indexFile()

			
			file.parent=        files
			file.discardable=   udeb
			file.compressable=  False
			file.detachedSign=  False
			file.clearSign=     False
			file.acquireByHash= files.acquireByHash
			file.relativePath=  relativePath
			files.indexes[key] = file
		return file

	def  ContentsIndex(files,component, arch :str, udeb :bool) ->indexFile :
		if arch == ArchitectureSource :
			udeb = False
		
		key =  "ci-{}-{}-{}".format(component, arch, udeb)
		file= files.indexes.get(key)
		if file is None:
			relativePath :str=""

			if udeb :
				relativePath = os.path.join(component,  "Contents-udeb-{}".format( arch))
			else :
				relativePath = os.path.join(component,  "Contents-{}".format(arch))
			

			file = indexFile()
			file.parent=        files
			file.discardable=   True
			file.compressable=  True
			file.onlyGzip=      True
			file.detachedSign=  False
			file.clearSign=     False
			file.acquireByHash= files.acquireByHash
			file.relativePath=  relativePath
			

			files.indexes[key] = file
		

		return file
	

	def   LegacyContentsIndex(files,arch :str, udeb :bool) ->indexFile :
		if arch == ArchitectureSource :
			udeb = False
		
		key =  "lci-{}-{}".format(arch, udeb)
		file  = files.indexes.get(key)
		if file is None:
			relativePath :str=''

			if udeb :
				relativePath =  "Contents-udeb-{}".format( arch)
			else :
				relativePath =  "Contents-{}".format(arch)
			

			file = indexFile()
			file.parent=        files
			file.discardable=   True
			file.compressable=  True
			file.onlyGzip=      True
			file.detachedSign=  False
			file.clearSign=     False
			file.acquireByHash= files.acquireByHash
			file.relativePath=  relativePath
			

			files.indexes[key] = file
		

		return file
	

	def ReleaseFile(files) ->indexFile :
		file=indexFile()
		
		file.parent=       files
		file.discardable=  False
		file.compressable= False
		file.detachedSign= True
		file.clearSign=    True
		file.relativePath= "Release"
		return file
		
	

	def  FinalizeAll(files,progress , signer  ):
		if progress is not None  :
			print('------------',(len(files.indexes)), False)
			# defer progress.ShutdownBar()
		

		for _, file in files.indexes.items() :
			err = file.Finalize(signer)
			if err is not None  :
				return
			
			if progress is not None  :
				pass
			
		

		files.indexes ={}

		return

	def  RenameFiles(files):
		err=None

		for oldName, newName in files.renameMap :
			err = files.publishedStorage.RenameFile(oldName, newName)
			if err is not None  :
				return "unable to rename: {}".format( err)
			
		

		return None
	

def packageIndexByHash(file :indexFile, ext :str, hash :str, sum :str):
	src = os.path.join(file.parent.basePath, file.relativePath)
	indexfile = os.path.dirname(src + ext)
	src = src + file.parent.suffix + ext
	filedir = os.path.dirname(os.path.join(file.parent.basePath, file.relativePath))
	dst = os.path.join(filedir, "by-hash", hash)
	sumfilePath = os.path.join(dst, sum)

	# // link already exists? do nothing
	exists, err = file.parent.publishedStorage.FileExists(sumfilePath)
	if err is not None  :
		return  "Acquire-By-Hash: error checking exists of file {}: {}".format( sumfilePath, err)
	
	if exists :
		return None
	

	# // create the link
	err = file.parent.publishedStorage.HardLink(src, sumfilePath)
	if err is not None  :
		return "Acquire-By-Hash: error creating hardlink {}: {}".format(sumfilePath, err)
	

	# // if a previous index file already exists exists, backup symlink
	indexPath = os.path.join(dst, indexfile)
	oldIndexPath = os.path.join(dst, indexfile+".old")
	exists  = file.parent.publishedStorage.FileExists(indexPath)
	if  exists :
		# // if exists, remove old symlink
		exists = file.parent.publishedStorage.FileExists(oldIndexPath)
		if exists :
			linkTarget :str=''
			linkTarget = file.parent.publishedStorage.ReadLink(oldIndexPath)
			if err is None  :
				# // If we managed to resolve the link target: delete it. This is the
				# // oldest physical index file we no longer need. Once we drop our
				# // old symlink we'll essentially forget about it existing at all.
				file.parent.publishedStorage.Remove(linkTarget)
			
			file.parent.publishedStorage.Remove(oldIndexPath)
		
		file.parent.publishedStorage.RenameFile(indexPath, oldIndexPath)
	

	# // create symlink
	err = file.parent.publishedStorage.SymLink(os.path.join(dst, sum), os.path.join(dst, indexfile))
	if err is not None  :
		return  "Acquire-By-Hash: error creating symlink {}: {}".format(os.path.join(dst, indexfile), err)
	
	return None


def newIndexFiles(publishedStorage , basePath, tempDir, suffix :str, acquireByHash :bool) ->indexFiles :
	indexFilesObj=indexFiles()

	 
	indexFilesObj.	publishedStorage= publishedStorage
	indexFilesObj.basePath=         basePath
	indexFilesObj.renameMap=        {}#make(map[string]string)
	indexFilesObj.generatedFiles=   {}#make(map[string]utils.ChecksumInfo)
	indexFilesObj.tempDir=          tempDir
	indexFilesObj.suffix=           suffix
	indexFilesObj.indexes=          {}#make(map[string]*indexFile)
	indexFilesObj.acquireByHash=    acquireByHash
	
	return indexFilesObj
